/**一、Bean的注入：把字段值（普通值或引用值）赋值到类中以便生成对象
 * XML（普通值直接赋值，类则通过ref直接引用或引用其工厂方法）：
 * 1.构造器注入
 * 2.setter注入
 * 注解/JavaConfig：
 * 1.@Value     //普通值（直接赋值或通过引用属性文件中的值）
 * 2.@AutoWired(属性注入、setter注入、构造器注入)    //类（直接引用类，也可以通过工厂方法生成后引用）
 * 二、根据注册到IOC中并已赋值好的类提取使用对象的方法：
 * 1.context.getBean    //主要用于XML方式注册的Bean
 * 2.@Autowired         //主要用于注解或javaconfig方式注册的Bean
 *   1）属性注入
 *   2）setter注入
 *   3）构造器注入
 * 三、 通过属性文件配置数据的方式之三：对于集合类型数据仅适用于单行表达
 * 1）注册Bean PropertiesFactoryBean或YamlPropertiesFactoryBean，其中指定property文件名
 * 2）@Autowire Property
 * 3）getProperty
 */
package com.example.demospringbootbean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.Properties;

@Component
public class BeanInjectDemo {

    //XML方式：context.getBean
    public void showInjectionByXML() {
        AppContextUtil.printlnb("---------XML方式注册Bean的注入--------");
        AbstractApplicationContext  context =
                new ClassPathXmlApplicationContext("beans2.xml");

        /*---------Spring Bean的注入概念及父子Bean---------*/
        //获取实例并改变message属性值
        ParentOfClassA objAP1 = (ParentOfClassA) context.getBean("helloworld");
        objAP1.setMessage("I'm object A");
        objAP1.getMessage();
        //因scope="prototype"，重新获取实例时，message属性的初值由bean.xml决定
        ParentOfClassA objAP2 =  context.getBean(ParentOfClassA.class);
        objAP2.getMessage();

        ClassA objA =  context.getBean("helloChina", ClassA.class);
        //message值来自其父类
        objA.getMessage();
        //message2值在beans.xml中定义
        objA.getMessage2();

        /*等效替代
        Resource resource=new ClassPathResource("Beans.xml");
        BeanFactory factory=new DefaultListableBeanFactory();
        BeanDefinitionReader bdr=new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);
        bdr.loadBeanDefinitions(resource);
        ClassParentOfA obj = (ClassParentOfA) factory.getBean("helloWorld");*/

        /*---------Spring Bean的4种注入方式，前两种常用--------*/
        //1.构造器注入
        System.out.println("1.构造器注入");
        ClassB objB1 =  (ClassB) context.getBean("classB1");
        objB1.spellCheck();

        //2.setter注入
        System.out.println("2.setter注入");
        ClassB objB2 = (ClassB) context.getBean("classB2");
        objB2.spellCheck();

        //3.静态工厂注入
        System.out.println("3.静态工厂注入");
        ClassB objB3 = (ClassB) context.getBean("classB3");
        objB3.spellCheck();

        //4.实例工厂注入
        System.out.println("4.实例工厂注入");
        ClassB objB4 = (ClassB) context.getBean("classB4");
        objB4.spellCheck();

        //嵌套bean（xml中嵌套格式）
        System.out.println("嵌套bean");
        ClassB objB = (ClassB) context.getBean("classB");
        objB.spellCheck();

        //集合注入
        System.out.println("集合注入");
        ClassCollection jc=(ClassCollection)context.getBean("javaCollection");
        jc.getAddressList();
        jc.getAddressSet();
        jc.getAddressMap();
        jc.getAddressProp();
        context.close();
    }

    //属性注入，隐藏了对外依赖，尽量避免使用
    @Autowired
    private  ClassBB classBB;

    @Autowired
    private  ClassCollection2 classCollection2;

    //构造器注入，用于必须的依赖，并设置为final
//    @Autowired
//    public BeanInjectDemo(ClassBB classBB){
//        this.classBB=classBB;
//        AppContextUtil.printlng("构造器注入ClassBB到BeanInjectDemo中");
//    }

    //setter注入，用于可选的依赖
//    @Autowired
//    public void setClassBB(ClassBB classBB){
//        this.classBB=classBB;
//        AppContextUtil.printlng("Setter注入ClassBB到BeanInjectDemo中");
//    }

    @Autowired
    private Properties myProperty;

    @Autowired
    private Properties myYamlProperty;

    @Autowired
    private Environment environment;

    //注解方式：context.getBean或@AutoWired，属性注入、构造器注入、setter注入
    public  void showInjectionByAnnotation() {

        AppContextUtil.printlnb("---------注解方式注册Bean的注入--------");
        classBB.showInfo();
        classBB.showCollection();

        AppContextUtil.printlnb("list="+classCollection2.addressList);
        AppContextUtil.printlnb("set="+classCollection2.addressSet);
        AppContextUtil.printlnb("map="+classCollection2.addressMap);

        //property格式单行表达支持集合类型数据,多行表达不支持集合类型数据（me.propertites）
        String phone1 = myProperty.getProperty("test.list");
        System.out.println(phone1);

        //yaml格式单行表达支持集合类型数据，多行表达不支持集合类型数据(me.yml)
        String phone2=myProperty.getProperty("test.map");
        System.out.println(phone2);

          AppContextUtil.printlny(environment.getProperty("test.map"));

          //不能与@Value同时使用时
//          ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//          ClassBB classBB2=(ClassBB) context.getBean("classBB");
//          classBB2.showInfo();
    }

    @Autowired
    private ClassB classB2;    //名称与bean name一致

    @Autowired
    @Qualifier("classB3")
    private ClassB classB;    //名称与bean name不一致时需在@Qualifier中注明bean name

    //JavaConfig方式：context.getBean或@AutoWired，属性注入、构造器注入、setter注入
    public  void showInjectionByJavaConfig() {
        AppContextUtil.printlnb("---------JavaConfig注册Bean的注入--------");
        classB2.showInfo();
        classB.showInfo();

          //不能与@Value同时使用时
//        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ClassConfiguration.class);
//        ClassB classB4=(ClassB) context.getBean("classB4");
//        classB4.showInfo();
    }

}
